//
// Copyright © 2025 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//

use actix_web::{HttpMessage, HttpRequest};
use hulyrs::services::jwt::Claims;
use serde_json::json;
use std::{fs, path::Path, sync::LazyLock};
use uuid::Uuid;

use crate::{config::CONFIG, redis::deprecated_symbol};

// common checker
pub fn check_workspace_core(claims_opt: Option<Claims>, key: &str) -> Result<(), &'static str> {
    if deprecated_symbol(key) {
        return Err("Invalid key: deprecated symbols");
    }

    if CONFIG.no_authorization {
        return Ok(());
    }

    let claims = claims_opt.ok_or("Missing authorization")?;

    if claims.is_system() {
        return Ok(());
    }

    let jwt_workspace = claims
        .workspace
        .as_ref()
        .ok_or("Missing workspace in token")?;
    let path_ws = key
        .split('/')
        .next()
        .ok_or("Invalid key: missing workspace")?;
    if path_ws.is_empty() {
        return Err("Invalid key: missing workspace");
    }

    let path_ws_uuid = Uuid::parse_str(path_ws).map_err(|_| "Invalid workspace UUID in key")?;
    if jwt_workspace != &path_ws_uuid {
        return Err("Workspace mismatch");
    }

    Ok(())
}

pub fn test_rego_claims(claim: &Claims, command: &str, key: &str) -> bool {
    let data = serde_json::to_value(&claim).unwrap_or_default();
    let mut rego = REGORUS_ENGINE.clone();

    rego.set_input(regorus::Value::from(json!({
        "command": command,
        "claim": data,
        "key": key,
    })));
    let result = rego.eval_rule(String::from("data.main.permit")).unwrap();

    result == regorus::Value::Bool(true)
}

pub fn test_rego_http(req: HttpRequest, command: &str, key: &str) -> bool {
    let claims = req
        .extensions()
        .get::<Claims>()
        .expect("Missing claims")
        .to_owned();
    test_rego_claims(&claims, command, key)
}

pub static POLICY_TEXT: LazyLock<String> = LazyLock::new(|| {
    let Some(policy_file) = CONFIG.policy_file.as_ref() else {
        return "package main\n\ndefault permit = true\n".to_string();
    };
    let path = Path::new(policy_file);
    if !path.exists() {
        panic!("Policy file not found: {}", path.display());
    }
    let policy_text = match fs::read_to_string(path) {
        Ok(text) => format!("package main\n\n{}", text),
        Err(e) => {
            panic!("Failed to read policy file {}: {}", path.display(), e);
        }
    };
    policy_text
});

pub static REGORUS_ENGINE: LazyLock<regorus::Engine> = LazyLock::new(|| {
    let mut e = regorus::Engine::new();
    e.add_policy("policy.rego".to_string(), POLICY_TEXT.to_string())
        .expect("can't add policy");
    e
});
